package userModify

import (
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"
	. "go_tempate/SQL/gormDB"
	. "go_tempate/SQL/redis"
	. "go_tempate/SQL/table"
	. "go_tempate/common/function"
	. "go_tempate/common/store"
	. "go_tempate/common/validator"
	"net/http"
	"strconv"
	"strings"
)

// 获取用户基本信息
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func GetModifyUserModify(ctx *gin.Context) {
	body := ObtainResponseBody(ctx) // 获取当前的返回结构体

	//用户信息集中操作
	//----------------------------------------------------------------------------------------------------------
	user, err := ObtainUserInfo(ctx, body) // 获取当前查询的用户信息

	//如果错误则弹出
	if err != nil {
		return
	}

	//进行用户数据结构体赋值
	//----------------------------------------------------------------------------------------------------------
	userModifyInfo := UserModifyInfo{
		Name:   user.Name,
		Age:    user.Age,
		Gender: user.Gender,
	} // 声明当前请求接受结构体

	//返回用户基本信息
	//----------------------------------------------------------------------------------------------------------
	body.Data = userModifyInfo

	zap.S().Infof("%v用户查询基本信息成功", user.Name)
}

// 修改用户信息
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func PostModifyUserModify(ctx *gin.Context) {
	body := ObtainResponseBody(ctx)     // 获取当前的返回结构体
	userModifyInfo := &UserModifyInfo{} // 声明当前请求接受结构体

	//请求体数据获取并验证
	//----------------------------------------------------------------------------------------------------------
	err := ObtainRequestContent(ctx, body, userModifyInfo)

	//如果错误则弹出
	if err != nil {
		return
	}

	//用户信息集中操作
	//----------------------------------------------------------------------------------------------------------
	user, err := ObtainUserInfo(ctx, body) // 获取当前查询的用户信息

	//如果错误则弹出
	if err != nil {
		return
	}

	//对数据库中的用户姓名、性别、年龄进行更新
	//----------------------------------------------------------------------------------------------------------
	err = user.UpdateUserInfo(userModifyInfo.Name, userModifyInfo.Age, userModifyInfo.Gender)

	//判断是否入库失败
	if err != nil {
		body.BatchSettings(SetUserInfoErr, SetUserMessageError, nil)
		return
	}

	//修改成功返回
	//----------------------------------------------------------------------------------------------------------
	body.Message = "修改成功"

	zap.S().Infof("%v用户修改个人信息成功", user.Name)
}

// 删除用户
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func DeleteUser(ctx *gin.Context) {
	body := ObtainResponseBody(ctx) // 获取当前的返回结构体

	// 执行 删除+冻结+解冻接口工具函数
	//----------------------------------------------------------------------------------------------------------
	userInfo := DelOrFreezeOrThaw(ctx, body)

	if userInfo == nil {
		return
	}

	//能查询到用户后，则进行执行删除功能函数
	//----------------------------------------------------------------------------------------------------------
	err := userInfo.DelUser()

	//若删除发生错误
	if err != nil {
		body.BatchSettings(DeleteUserErr, FailedToDeleteUser, nil)
		return
	}

	//删除成功后
	//----------------------------------------------------------------------------------------------------------
	body.Message = "用户删除成功"

	zap.S().Infof("ID:%v;用户名:%v;信息删除成功", userInfo.ID, userInfo.Name)
}

// 冻结用户
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func GetFreezeUser(ctx *gin.Context) {
	body := ObtainResponseBody(ctx) // 获取当前的返回结构体

	// 执行 删除+冻结+解冻接口工具函数
	//----------------------------------------------------------------------------------------------------------
	userInfo := DelOrFreezeOrThaw(ctx, body)

	if userInfo == nil {
		return
	}

	//在redis中添加对应的冻结信息
	//----------------------------------------------------------------------------------------------------------
	freeze := FreezeName + strconv.Itoa(int(userInfo.ID))
	err := SetRedisItems(freeze, "3")

	if err != nil {
		//加入redis失败时
		body.BatchSettings(MessageJoinRedis, FrozenStateJoinRedisError, nil)
		return
	}

	//修改用户状态为冻结状态
	//----------------------------------------------------------------------------------------------------------
	err = userInfo.LoginInfo.SetLoginInfoState(3)

	if err != nil {
		//删除对应的redis数据
		_ = DeleteRedisItem(freeze)

		//如果数据库操作失败
		body.BatchSettings(MessageJoinDB, FrozenStateJoinDBError, nil)
		return
	}

	body.Message = "用户冻结成功"

	zap.S().Infof("ID:%v;用户名:%v;冻结成功", userInfo.ID, userInfo.Name)

}

// 解冻用户
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func UserThaw(ctx *gin.Context) {
	body := ObtainResponseBody(ctx) // 获取当前的返回结构体

	// 执行 删除+冻结+解冻接口工具函数
	//----------------------------------------------------------------------------------------------------------
	userInfo := DelOrFreezeOrThaw(ctx, body)

	if userInfo == nil {
		return
	}

	//检查当前用户状态是否为冻结状态
	//----------------------------------------------------------------------------------------------------------
	if userInfo.LoginInfo.State != 3 {
		//如果不是冻结状态
		body.BatchSettings(UserStatusErr, UserStatusNotFreeze, nil)
		return
	}

	//删除redis中对应的冻结列表
	//----------------------------------------------------------------------------------------------------------
	freeze := FreezeName + strconv.Itoa(int(userInfo.ID)) //拼接此时需要操作redis成员名称

	err := DeleteRedisItem(freeze)

	if err != nil {
		// 如果redis删除报错
		body.BatchSettings(UseRedisErr, UseRedisError, nil)
		return
	}

	//修改数据库中的状态
	//----------------------------------------------------------------------------------------------------------
	err = userInfo.LoginInfo.SetLoginInfoState(0)

	if err != nil {
		// 如果数据库操作失败,则恢复redis原本设置
		_ = SetRedisItems(freeze, "3")

		body.BatchSettings(UseDbErr, UseDbError, nil)
		return
	}

	body.Message = "用户解冻成功"

	zap.S().Infof("ID:%V ; 用户名:%v ; 解冻成功", userInfo.ID, userInfo.Name)
}

// 获取用户设置的密保类型合集
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func GetModifySecurity(ctx *gin.Context) {
	body := ObtainResponseBody(ctx) // 获取当前的返回结构体

	//用户信息集中操作
	//----------------------------------------------------------------------------------------------------------
	user, err := ObtainUserInfo(ctx, body) // 获取当前查询的用户信息

	//如果错误则弹出
	if err != nil {
		return
	}

	//用户密保类型ID查询
	//----------------------------------------------------------------------------------------------------------
	sqlStr := make([]string, len(user.UserSecurity))       //sql查询语句
	sqlDate := make([]interface{}, len(user.UserSecurity)) // sql查询数据

	//循环查询 用户的密保关联表 给sqlStr和sqlDate赋值
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	for i, v := range user.UserSecurity {
		sqlStr[i] = "id = ?"
		sqlDate[i] = v.SecurityTypeId
	}

	//声明接收查询密保类型数据
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	var userSecurityList = []UserSecurityItem{}

	//将 sqlStr 用 or 进行转化为字符串，并将sqlDate打散查询数据
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	err = Gorm.Model(&SecurityType{}).Where(strings.Join(sqlStr, " or "), sqlDate...).Find(&userSecurityList).Error

	//如果查询失败
	if err != nil {
		body.BatchSettings(GetUserInfoErr, GetUserConfidentialityError, nil)
		return
	}

	//设置返回date
	//----------------------------------------------------------------------------------------------------------
	body.Data = map[string][]UserSecurityItem{
		"userSecurityList": userSecurityList,
	}

	zap.S().Infof("%v用户查询个人密保问题", user.Name)
}

// 修改密保信息
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func PostModifySecurity(ctx *gin.Context) {
	body := ObtainResponseBody(ctx)                 // 获取当前的返回结构体
	modifyUserSecurityList := &ModifyUserSecurity{} // 请求体参数接收

	//请求体数据获取并验证
	//----------------------------------------------------------------------------------------------------------
	err := ObtainRequestContent(ctx, body, modifyUserSecurityList)

	//如果错误则弹出
	if err != nil {
		return
	}

	//用户信息集中操作
	//----------------------------------------------------------------------------------------------------------
	user, err := ObtainUserInfo(ctx, body) // 获取当前查询的用户信息

	//如果错误则弹出
	if err != nil {
		return
	}

	//用户当前设置密保合计
	//----------------------------------------------------------------------------------------------------------
	oldUserSecurity := map[uint]string{}        // 数据记录map
	removeDuplicatesSecurity := map[uint]bool{} // 去重map

	//将目前用户id作为oldUserSecurity的key，并将存储的答案传入
	for _, v := range user.UserSecurity {
		oldUserSecurity[v.SecurityTypeId] = v.Answer
	}

	//对前端传入的验证数据进行验证
	//----------------------------------------------------------------------------------------------------------
	for _, item := range modifyUserSecurityList.ModifyUserSecurityList {
		//验证每个成员是否符合条件
		//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		errs := ValidatorVerify(item)

		if errs != nil {
			//进行批量设置当前返回结构体
			body.BatchSettings(GetUserInfoErr, errs.(validator.ValidationErrors)[0].Translate(Trans), nil)
			return
		}

		//验证传入的是否存在相同的类型
		//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		removeStatus := removeDuplicatesSecurity[item.TypeId] // 获取此时的成员是否重复

		//如果重复
		if removeStatus {
			//进行批量设置当前返回结构体
			body.BatchSettings(GetUserInfoErr, UserConfidentialityRepeat, nil)
			return
		}

		//记录此时成员的状态
		removeDuplicatesSecurity[item.TypeId] = true

		//数据记录并验证
		//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		oldAnswerStr := oldUserSecurity[item.TypeId] // 获取此时 oldUserSecurity 的记录值

		//如果此时传入的id在 oldUserSecurity 中并没有记录，则表示此问题并不是此用户设置
		if oldAnswerStr == "" {
			body.BatchSettings(GetUserInfoErr, UserHaveNotConfidentiality, nil)
			return
		}

		//进行比对库中的加密密保问题和传入的老密保问题是否一致
		//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		ok, err := ComparePasswordAndHash(item.OldAnswer, oldAnswerStr)

		//如果验证报错或验证失败
		if err != nil || !ok {
			body.BatchSettings(GetUserInfoErr, UserConfidentialityTestError, nil)
			return
		}

		//验证都是成功的则将 oldUserSecurity 对应成员的值修改为最新加密的值
		//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
		newStr, err := GenerateFromPassword(item.NewAnswer, true)

		//如果出现报错
		if err != nil {
			body.BatchSettings(SetUserInfoErr, UserConfidentialityEncryptionError, nil)
			return
		}

		//同步加密后的值
		oldUserSecurity[item.TypeId] = newStr
	}

	//最后对 oldUserSecurity 进行循环入库并返回成功提示
	//----------------------------------------------------------------------------------------------------------
	for u, msg := range oldUserSecurity {
		Gorm.Model(&UserSecurity{}).Where("user_info_id = ? and security_type_id = ?", user.ID, u).Update("answer", msg)
	}

	body.Message = "密保问题修改成功"

	zap.S().Infof("%v:用户密保修改成功", user.Name)
}

// 修改密码
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func PostChangePassword(ctx *gin.Context) {
	body := ObtainResponseBody(ctx)    // 获取当前的返回结构体
	changePassword := ChangePassword{} // 请求体参数接收

	//请求体数据获取并验证
	//----------------------------------------------------------------------------------------------------------
	err := ObtainRequestContent(ctx, body, &changePassword)

	//如果错误则弹出
	if err != nil {
		return
	}

	//用户信息集中操作
	//----------------------------------------------------------------------------------------------------------
	user, err := ObtainUserInfo(ctx, body) // 获取当前查询的用户信息

	//如果错误则弹出
	if err != nil {
		return
	}

	//使用密码+密保验证工具函数
	//----------------------------------------------------------------------------------------------------------
	if err = PasswordAndSecurity(user, &changePassword); err != nil {
		//如果验证失败
		body.BatchSettings(http.StatusBadRequest, err.Error(), nil)
		return
	}

	//都验证成功的话
	//----------------------------------------------------------------------------------------------------------
	body.Message = "修改成功,请重新登录"

	zap.S().Infof("%v用户:密码修改成功", user.Name)
}

// 忘记密码
// ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
func PostForgotPassword(ctx *gin.Context) {
	body := ObtainResponseBody(ctx)    // 获取当前的返回结构体
	forgotPassword := ForgotPassword{} // 请求体参数接收

	//请求体数据获取并验证
	//----------------------------------------------------------------------------------------------------------
	err := ObtainRequestContent(ctx, body, &forgotPassword)

	//如果错误则弹出
	if err != nil {
		return
	}

	//验证修改密码结构体
	//----------------------------------------------------------------------------------------------------------
	errs := ValidatorVerify(&forgotPassword.ChangePassword)

	if errs != nil {
		//进行批量设置当前返回结构体
		body.BatchSettings(SetUserInfoErr, errs.(validator.ValidationErrors)[0].Translate(Trans), nil)
		return
	}

	//获取用户信息
	//----------------------------------------------------------------------------------------------------------
	users := []UserInfo{} // 所有用户查询表

	//查询和对应用户相同的账号信息
	err = Gorm.Where("name = ?", forgotPassword.Name).Preload("LoginInfo").Preload("UserSecurity").Find(&users).Error

	if err != nil {
		//进行批量设置当前返回结构体
		body.BatchSettings(SetUserInfoErr, GetUserMessageError, nil)
		return
	} else if len(users) == 0 {
		//进行批量设置当前返回结构体
		body.BatchSettings(SetUserInfoErr, NotUserInfoMessage, nil)
		return
	}

	//验证对应账号是否存在
	//----------------------------------------------------------------------------------------------------------
	var user *UserInfo // 承接验证通过后的账号信息

	//循环所有找到的用户信息
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	for _, v := range users {
		//如果账户内容匹配，则将值赋予user
		if v.LoginInfo.Account == forgotPassword.Account {
			user = &v
		}
	}

	//如果此时user还是没有值
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	if user == nil {
		//进行批量设置当前返回结构体
		body.BatchSettings(SetUserInfoErr, NotUserInfoMessage, nil)
		return
	}

	//使用密码+密保验证工具函数
	//----------------------------------------------------------------------------------------------------------
	if err = PasswordAndSecurity(user, &forgotPassword.ChangePassword); err != nil {
		//如果验证失败
		body.BatchSettings(http.StatusBadRequest, err.Error(), nil)
		return
	}

	//都验证成功的话
	//----------------------------------------------------------------------------------------------------------
	body.Message = "修改成功,请重新登录"

	zap.S().Infof("%v用户密码修改成功", user.Name)
}
